<

カスタム プラットフォーム固有のコードの作成

このガイドでは、プラットフォーム固有のカスタム コードを作成する方法について説明します。 いくつかのプラットフォーム固有の機能が利用可能です 既存のパッケージを通じて。 見るパッケージの使用

Flutter は柔軟なシステムを使用しており、 直接動作する言語によるプラットフォーム固有の API これらの API を使用すると:

  • Android 上の Kotlin または Java
  • iOS 上の Swift または Objective-C
  • Windows 上の C++
  • macOS 上の Objective-C
  • Linux 上の C

Flutter の組み込みプラットフォーム固有 API サポート コード生成に依存せず、 むしろ、柔軟なメッセージ受け渡しスタイルを重視しています。 あるいは、鳩のパッケージ構造化されたタイプセーフメッセージの送信コード生成を使用すると:

  • アプリの Flutter 部分はメッセージをそのホスト、 プラットフォーム チャネル経由でのアプリの非 Dart 部分。

  • ホストプラットフォーム チャネルをリッスンし、メッセージを受信します。 次に、次を使用して、任意の数のプラットフォーム固有の API を呼び出します。 ネイティブ プログラミング言語 — そして応答をクライアント、アプリの Flutter 部分。

アーキテクチャの概要: プラットフォーム チャネル

メッセージはクライアント (UI) 間で受け渡されます。 プラットフォームを使用するホスト (プラットフォーム) この図に示すように、チャネルは次のようになります。

Platform channels architecture

メッセージと応答は非同期で渡されます。 ユーザー インターフェイスの応答性を維持するため。

クライアント側では、MethodChannel送信を有効にする メソッド呼び出しに対応するメッセージ。ホーム側には、MethodChannelAndroid の場合 (MethodChannelAndroid) とFlutterMethodChanneliOS の場合 (MethodChanneliOS) メソッド呼び出しの受信と送信を可能にする 結果。これらのクラスを使用すると、プラットフォーム プラグインを開発できます。 「定型コード」はほとんどありません。

プラットフォーム チャネルのデータ型のサポートとコーデック

標準プラットフォーム チャネルは、サポートする標準メッセージ コーデックを使用します。 ブール値などの単純な JSON のような値の効率的なバイナリ シリアル化。 数値、文字列、バイトバッファ、およびこれらのリストとマップ (見るStandardMessageCodec詳細については)。 これらの値のシリアル化とシリアル化解除 値を送受信すると、メッセージが自動的に生成されます。

次の表は、Dart 値がどのように受信されるかを示しています。 プラットフォーム側とその逆:

ダーツ ジャワ
ヌル ヌル
ブール java.lang.Boolean
整数 java.lang.Integer
int (32 ビットでは不十分な場合) java.lang.Long
ダブル java.lang.Double
java.lang.String
Uint8List バイト[]
Int32List int[]
Int64List 長さ[]
Float32List 浮く[]
Float64List ダブル[]
リスト java.util.ArrayList
地図 java.util.HashMap
ダーツ コトリン
ヌル ヌル
ブール ブール値
整数 内部
int (32 ビットでは不十分な場合) 長さ
ダブル ダブル
Uint8List バイト配列
Int32List IntArray
Int64List 長い配列
Float32List 浮動小数点配列
Float64List ダブルアレイ
リスト リスト
地図 ハッシュマップ
ダーツ 目的-C
ヌル nil (ネストされている場合は NSNull)
ブール NSNumbernumberWithBool:
整数 NSNumbernumberWithInt:
int (32 ビットでは不十分な場合) NSNumbernumberWithLong:
ダブル NSNumbernumberWithDouble:
NS文字列
Uint8List FlutterStandardTypedData 型付きDataWithBytes:
Int32List FlutterStandardTypedData 型付きDataWithInt32:
Int64List FlutterStandardTypedData 型付きDataWithInt64:
Float32List FlutterStandardTypedData 型付きDataWithFloat32:
Float64List FlutterStandardTypedData 型付きDataWithFloat64:
リスト NSArray
地図 NSDictionary
ダーツ 迅速
ヌル なし
ブール NSNumber(値: ブール値)
整数 NSNumber(値: Int32)
int (32 ビットでは不十分な場合) NSNumber(値: Int)
ダブル NSNumber(値: Double)
Uint8List FlutterStandardTypedData(バイト: データ)
Int32List FlutterStandardTypedData(int32: データ)
Int64List FlutterStandardTypedData(int64: データ)
Float32List FlutterStandardTypedData(float32:データ)
Float64List FlutterStandardTypedData(float64:データ)
リスト 配列
地図 辞書
ダーツ C++
ヌル EncodableValue()
ブール EncodableValue(ブール値)
整数 EncodableValue(int32_t)
int (32 ビットでは不十分な場合) EncodableValue(int64_t)
ダブル EncodableValue(double)
EncodableValue(std::string)
Uint8List EncodableValue(std::vector)
Int32List EncodableValue(std::vector)
Int64List EncodableValue(std::vector)
Float32List EncodableValue(std::vector)
Float64List EncodableValue(std::vector)
リスト EncodableValue(std::vector)
地図 EncodableValue(std::map<EncodableValue, EncodableValue>)
ダーツ C (Gオブジェクト)
ヌル FlValue()
ブール FlValue(ブール値)
整数 FlValue(int64_t)
ダブル FlValue(倍精度)
FlValue(gchar*)
Uint8List FlValue(uint8_t*)
Int32List FlValue(int32_t*)
Int64List FlValue(int64_t*)
Float32List FlValue(float*)
Float64List FlValue(double*)
リスト Fl値(Fl値)
地図 FlValue(FlValue, FlValue)

例: プラットフォーム チャネルを使用したプラットフォーム固有のコードの呼び出し

次のコードは、呼び出し方法を示しています。 取得および表示するプラットフォーム固有の API 現在のバッテリーレベル。それは使用しています アンドロイドBatteryManagerAPI、 iOSdevice.batteryLevelAPI、 窓GetSystemPowerStatusAPI、 そしてリナックスUPower単一の API プラットフォームメッセージ、getBatteryLevel()

この例では、プラットフォーム固有のコードを内部に追加します。 メインアプリ自体。再利用したい場合は、 複数のアプリ用のプラットフォーム固有のコード、 プロジェクトの作成手順が若干異なります (見るパッケージの開発)、 ただし、プラットフォームのチャネルコード 今でも同じように書かれています。

ステップ 1: 新しいアプリ プロジェクトを作成する

新しいアプリを作成することから始めます。

  • ターミナルで次を実行します。flutter create batterylevel

デフォルトでは、テンプレートは Kotlin を使用した Android コードの作成をサポートしています。 または Swift を使用した iOS コード。 Java または Objective-C を使用するには、 使用-iおよび/または-aフラグ:

  • ターミナルで次を実行します。flutter create -i objc -a java batterylevel

ステップ 2: Flutter プラットフォーム クライアントを作成する

アプリのStateクラスは現在のアプリの状態を保持します。 それを拡張して現在のバッテリー状態を保持します。

まず、チャネルを構築します。使うMethodChannel単一の バッテリーレベルを返すプラットフォームメソッド。

チャネルのクライアント側とホスト側は、 チャネルコンストラクターで渡されるチャネル名。 1 つのアプリで使用されるすべてのチャンネル名は、 一意であること;チャンネル名の前に一意の「ドメイン」を付けます プレフィックス」、例:samples.flutter.dev/battery

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
  static const platform = MethodChannel('samples.flutter.dev/battery');
  // Get battery level.

次に、メソッド チャネルでメソッドを呼び出します。 を使用して呼び出す具体的なメソッドを指定する のString識別子getBatteryLevel。 呼び出しは失敗する可能性があります。たとえば、 プラットフォームがサポートしていない場合は、 プラットフォーム API (シミュレーターで実行する場合など)、 それでラップしますinvokeMethodtry-catch ステートメントで呼び出します。

返された結果を使用して、ユーザー インターフェイスの状態を更新します。_batteryLevel中身setState

// Get battery level.
String _batteryLevel = 'Unknown battery level.';

Future<void> _getBatteryLevel() async {
  String batteryLevel;
  try {
    final int result = await platform.invokeMethod('getBatteryLevel');
    batteryLevel = 'Battery level at $result % .';
  } on PlatformException catch (e) {
    batteryLevel = "Failed to get battery level: '${e.message}'.";
  }

  setState(() {
    _batteryLevel = batteryLevel;
  });
}

最後に、buildテンプレートからメソッドへ バッテリーを表示する小さなユーザーインターフェイスが含まれています 文字列内の状態と、値を更新するためのボタン。

@override
Widget build(BuildContext context) {
  return Material(
    child: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          ElevatedButton(
            onPressed: _getBatteryLevel,
            child: const Text('Get Battery Level'),
          ),
          Text(_batteryLevel),
        ],
      ),
    ),
  );
}

ステップ 3: Android プラットフォーム固有の実装を追加する

まず、Flutter アプリの Android ホスト部分を開きます Android Studio の場合:

  1. Android Studioを起動する

  2. メニュー項目を選択しますファイル > 開く…

  3. Flutter アプリが保存されているディレクトリに移動します。 そして、アンドロイドその中にあるフォルダー。クリックOK

  4. ファイルを開くMainActivity.ktにありますコトリン内のフォルダー プロジェクトビュー。

内部configureFlutterEngine()メソッドを作成するMethodChannelそして電話するsetMethodCallHandler()。必ず同じチャンネル名を使用してください。 Flutterクライアント側で使用されました。

MyActivity.kt
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
  private val CHANNEL = "samples.flutter.dev/battery"

  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      call, result ->
      // This method is invoked on the main thread.
      // TODO
    }
  }
}

Android バッテリー API を使用する Android Kotlin コードを追加します。 バッテリーレベルを取得します。このコードはあなたとまったく同じです ネイティブ Android アプリに書き込むことになります。

まず、必要なインポートをファイルの先頭に追加します。

MyActivity.kt
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES

次に、次のメソッドをMainActivityクラス、 の下にconfigureFlutterEngine()方法:

MyActivity.kt
private fun getBatteryLevel(): Int {
  val batteryLevel: Int
  if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
    val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
  } else {
    val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
    batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
  }

  return batteryLevel
}

最後に、setMethodCallHandler()先ほど追加したメソッド。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel()、 それでそれをテストしてくださいcall口論。 このプラットフォーム メソッドの実装では、 前のステップで記述された Android コード。両方の応答を返します。 を使用した成功とエラーのケースresult口論。 不明なメソッドが呼び出された場合は、代わりにそれを報告します。

次のコードを削除します。

MyActivity.kt
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
  call, result ->
  // This method is invoked on the main thread.
  // TODO
}

そして、次のように置き換えます。

MyActivity.kt
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
  // This method is invoked on the main thread.
  call, result ->
  if (call.method == "getBatteryLevel") {
    val batteryLevel = getBatteryLevel()

    if (batteryLevel != -1) {
      result.success(batteryLevel)
    } else {
      result.error("UNAVAILABLE", "Battery level not available.", null)
    }
  } else {
    result.notImplemented()
  }
}

まず、Flutter アプリの Android ホスト部分を開きます Android Studio の場合:

  1. Android Studioを起動する

  2. メニュー項目を選択しますファイル > 開く…

  3. Flutter アプリが保存されているディレクトリに移動します。 そして、アンドロイドその中にあるフォルダー。クリックOK

  4. を開きますMainActivity.javaファイルは次の場所にありますジャワ内のフォルダー プロジェクトビュー。

次に、MethodChannelそして、MethodCallHandlerの中でconfigureFlutterEngine()方法。 必ず、使用したものと同じチャンネル名を使用してください。 クライアント側を flutterします。

MainActivity.java
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "samples.flutter.dev/battery";

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
  super.configureFlutterEngine(flutterEngine);
    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
        .setMethodCallHandler(
          (call, result) -> {
            // This method is invoked on the main thread.
            // TODO
          }
        );
  }
}

Android バッテリー API を使用する Android Java コードを次の場所に追加します。 バッテリーレベルを取得します。このコードはあなたとまったく同じです ネイティブ Android アプリに書き込むことになります。

まず、必要なインポートをファイルの先頭に追加します。

MainActivity.java
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;

次に、アクティビティ クラスに新しいメソッドとして次のコードを追加します。 の下にconfigureFlutterEngine()方法:

MainActivity.java
private int getBatteryLevel() {
  int batteryLevel = -1;
  if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
    BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
  } else {
    Intent intent = new ContextWrapper(getApplicationContext()).
        registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
        intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
  }

  return batteryLevel;
}

最後に、setMethodCallHandler()先ほど追加したメソッド。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel()、 それでそれをテストしてくださいcall口論。の実装 このプラットフォーム メソッドは、記述された Android コードを呼び出します。 前のステップで、両方の応答を返します。 を使用した成功とエラーのケースresult口論。 不明なメソッドが呼び出された場合は、代わりにそれを報告します。

次のコードを削除します。

MainActivity.java
(call, result) -> {
  // This method is invoked on the main thread.
  // TODO
}

そして、次のように置き換えます。

MainActivity.java
(call, result) -> {
  // This method is invoked on the main thread.
  if (call.method.equals("getBatteryLevel")) {
    int batteryLevel = getBatteryLevel();

    if (batteryLevel != -1) {
      result.success(batteryLevel);
    } else {
      result.error("UNAVAILABLE", "Battery level not available.", null);
    }
  } else {
    result.notImplemented();
  }
}

これで、Android でアプリを実行できるようになります。 Androidをご利用の場合 エミュレータ、拡張コントロールパネルでバッテリーレベルを設定します からアクセス可能ツールバーのボタン。

ステップ 4: iOS プラットフォーム固有の実装を追加する

まず、Xcode で Flutter アプリの iOS ホスト部分を開きます。

  1. Xcodeを起動します。

  2. メニュー項目を選択しますファイル > 開く…

  3. Flutter アプリが保存されているディレクトリに移動し、iosその中にあるフォルダー。クリックOK

Objective-C を使用する標準テンプレート セットアップに Swift のサポートを追加します。

  1. 「ランナー」>「ランナー」を展開しますプロジェクトナビゲーターで。

  2. ファイルを開くAppDelegate.swift以下にありますランナー > ランナープロジェクトナビゲーターで。

オーバーライドapplication:didFinishLaunchingWithOptions:機能し、創造する あるFlutterMethodChannelチャンネル名に紐づくsamples.flutter.dev/battery:

AppDelegate.swift
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
                                              binaryMessenger: controller.binaryMessenger)
    batteryChannel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      // This method is invoked on the UI thread.
      // Handle battery messages.
    })

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

次に、iOS バッテリー API を使用して取得する iOS Swift コードを追加します。 バッテリーレベル。このコードはあなたとまったく同じです ネイティブ iOS アプリに書き込むことになります。

以下を新しいメソッドとして下部に追加します。AppDelegate.swift:

AppDelegate.swift
private func receiveBatteryLevel(result: FlutterResult) {
  let device = UIDevice.current
  device.isBatteryMonitoringEnabled = true
  if device.batteryState == UIDevice.BatteryState.unknown {
    result(FlutterError(code: "UNAVAILABLE",
                        message: "Battery level not available.",
                        details: nil))
  } else {
    result(Int(device.batteryLevel * 100))
  }
}

最後に、setMethodCallHandler()先ほど追加したメソッド。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel()、 それでそれをテストしてくださいcall口論。 このプラットフォーム メソッドの実装では、 前の手順で作成した iOS コード。未知の方法の場合 が呼び出された場合は、代わりにそれを報告します。

AppDelegate.swift
batteryChannel.setMethodCallHandler({
  [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
  // This method is invoked on the UI thread.
  guard call.method == "getBatteryLevel" else {
    result(FlutterMethodNotImplemented)
    return
  }
  self?.receiveBatteryLevel(result: result)
})

まず、Xcode で Flutter アプリの iOS ホスト部分を開きます。

  1. Xcodeを起動します。

  2. メニュー項目を選択しますファイル > 開く…

  3. Flutter アプリが保存されているディレクトリに移動します。 そして、iosその中にあるフォルダー。クリックOK

  4. Xcode プロジェクトがエラーなしでビルドされることを確認してください。

  5. ファイルを開くAppDelegate.m、以下にありますランナー > ランナープロジェクトナビゲーターで。

を作成しますFlutterMethodChannel内にハンドラーを追加しますapplication didFinishLaunchingWithOptions:方法。 必ず同じチャンネル名を使用してください Flutter クライアント側で使用されていたものと同じです。

AppDelegate.m
#import <Flutter/Flutter.h>
#import "GeneratedPluginRegistrant.h"

@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

  FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
                                          methodChannelWithName:@"samples.flutter.dev/battery"
                                          binaryMessenger:controller.binaryMessenger];

  [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
    // This method is invoked on the UI thread.
    // TODO
  }];

  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

次に、iOS バッテリー API を使用する iOS ObjectiveC コードを追加します。 バッテリーレベルを取得します。このコードはあなたとまったく同じです ネイティブ iOS アプリに書き込むことになります。

に次のメソッドを追加しますAppDelegate授業、直前@end:

AppDelegate.m
- (int)getBatteryLevel {
  UIDevice* device = UIDevice.currentDevice;
  device.batteryMonitoringEnabled = YES;
  if (device.batteryState == UIDeviceBatteryStateUnknown) {
    return -1;
  } else {
    return (int)(device.batteryLevel * 100);
  }
}

最後に、setMethodCallHandler()先ほど追加したメソッド。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel()、 それでそれをテストしてくださいcall口論。の実装 このプラットフォーム メソッドは、前の手順で作成した iOS コードを呼び出します。 そして、成功とエラーの両方の場合に応答を返します。 のresult口論。不明なメソッドが呼び出された場合は、代わりにそれを報告します。

AppDelegate.m
__weak typeof(self) weakSelf = self;
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
  // This method is invoked on the UI thread.
  if ([@"getBatteryLevel" isEqualToString:call.method]) {
    int batteryLevel = [weakSelf getBatteryLevel];

    if (batteryLevel == -1) {
      result([FlutterError errorWithCode:@"UNAVAILABLE"
                                 message:@"Battery level not available."
                                 details:nil]);
    } else {
      result(@(batteryLevel));
    }
  } else {
    result(FlutterMethodNotImplemented);
  }
}];

これで、iOS でアプリを実行できるようになります。 iOSシミュレータを使用する場合は、 バッテリーAPIをサポートしていないことに注意してください。 アプリには「バッテリー残量が利用できません」と表示されます。

ステップ 5: Windows プラットフォーム固有の実装を追加する

まず、Visual Studio で Flutter アプリの Windows ホスト部分を開きます。

  1. 走るflutter build windowsプロジェクトディレクトリに一度追加して生成します Visual Studio ソリューション ファイル。

  2. Visual Studio を起動します。

  3. 選択するプロジェクトまたはソリューションを開く

  4. Flutter アプリが保存されているディレクトリに移動し、建てるフォルダー、次にウィンドウズフォルダーを選択し、batterylevel.slnファイル。 クリック開ける

プラットフォーム チャネル メソッドの C++ 実装を追加します。

  1. 拡大バッテリーレベル > ソースファイルソリューション エクスプローラーで。

  2. ファイルを開くflutter_window.cpp

まず、必要なインクルードをファイルの先頭に追加します。 後#include "flutter_window.h":

flutter_window.cpp
#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>

#include <memory>

を編集しますFlutterWindow::OnCreateメソッドと作成 あるflutter::MethodChannelチャンネル名に紐づくsamples.flutter.dev/battery:

flutter_window.cpp
bool FlutterWindow::OnCreate() {
  // ...
  RegisterPlugins(flutter_controller_->engine());

  flutter::MethodChannel<> channel(
      flutter_controller_->engine()->messenger(), "samples.flutter.dev/battery",
      &flutter::StandardMethodCodec::GetInstance());
  channel.SetMethodCallHandler(
      [](const flutter::MethodCall<>& call,
         std::unique_ptr<flutter::MethodResult<>> result) {
        // TODO
      });

  SetChildContent(flutter_controller_->view()->GetNativeWindow());
  return true;
}

次に、Windows バッテリー API を使用する C++ コードを追加します。 バッテリーレベルを取得します。このコードは次とまったく同じです ネイティブ Windows アプリケーションで記述することになります。

以下を新しい関数として先頭に追加しますflutter_window.cppの直後#includeセクション:

flutter_window.cpp
static int GetBatteryLevel() {
  SYSTEM_POWER_STATUS status;
  if (GetSystemPowerStatus(&status) == 0 || status.BatteryLifePercent == 255) {
    return -1;
  }
  return status.BatteryLifePercent;
}

最後に、setMethodCallHandler()先ほど追加したメソッド。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel()、 それでそれをテストしてくださいcall口論。 このプラットフォーム メソッドの実装では、 前の手順で記述した Windows コード。未知の方法の場合 が呼び出された場合は、代わりにそれを報告します。

次のコードを削除します。

flutter_window.cpp
channel.SetMethodCallHandler(
    [](const flutter::MethodCall<>& call,
       std::unique_ptr<flutter::MethodResult<>> result) {
      // TODO
    });

そして、次のように置き換えます。

flutter_window.cpp
channel.SetMethodCallHandler(
    [](const flutter::MethodCall<>& call,
       std::unique_ptr<flutter::MethodResult<>> result) {
      if (call.method_name() == "getBatteryLevel") {
        int battery_level = GetBatteryLevel();
        if (battery_level != -1) {
          result->Success(battery_level);
        } else {
          result->Error("UNAVAILABLE", "Battery level not available.");
        }
      } else {
        result->NotImplemented();
      }
    });

これで、Windows 上でアプリケーションを実行できるようになります。 デバイスにバッテリーがない場合は、 「バッテリー残量が利用できません」と表示されます。

ステップ 6: macOS プラットフォーム固有の実装を追加する

まず、Xcode で Flutter アプリの macOS ホスト部分を開きます。

  1. Xcodeを起動します。

  2. メニュー項目を選択しますファイル > 開く…

  3. Flutter アプリが保存されているディレクトリに移動し、マックOSその中にあるフォルダー。クリックOK

プラットフォーム チャネル メソッドの Swift 実装を追加します。

  1. 「ランナー」>「ランナー」を展開しますプロジェクトナビゲーターで。

  2. ファイルを開くMainFlutterWindow.swift以下にありますランナー > ランナープロジェクトナビゲーターで。

まず、必要なインポートをファイルの先頭、直後に追加します。import FlutterMacOS:

MainFlutterWindow.swift
import IOKit.ps

を作成しますFlutterMethodChannelチャンネル名に紐づくsamples.flutter.dev/batteryの中にawakeFromNib方法:

MainFlutterWindow.swift
  override func awakeFromNib() {
    // ...
    self.setFrame(windowFrame, display: true)
  
    let batteryChannel = FlutterMethodChannel(
      name: "samples.flutter.dev/battery",
      binaryMessenger: flutterViewController.engine.binaryMessenger)
    batteryChannel.setMethodCallHandler { (call, result) in
      // This method is invoked on the UI thread.
      // Handle battery messages.
    }

    RegisterGeneratedPlugins(registry: flutterViewController)

    super.awakeFromNib()
  }
}

次に、IOKit バッテリー API を使用して情報を取得する macOS Swift コードを追加します。 バッテリーレベル。このコードはあなたとまったく同じです ネイティブ macOS アプリで記述することになります。

以下を新しいメソッドとして下部に追加します。MainFlutterWindow.swift:

MainFlutterWindow.swift
private func getBatteryLevel() -> Int? {
  let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()
  let sources: Array<CFTypeRef> = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array
  if let source = sources.first {
    let description =
      IOPSGetPowerSourceDescription(info, source).takeUnretainedValue() as! [String: AnyObject]
    if let level = description[kIOPSCurrentCapacityKey] as? Int {
      return level
    }
  }
  return nil
}

最後に、setMethodCallHandler先ほど追加したメソッド。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel()、 それでそれをテストしてくださいcall口論。 このプラットフォーム メソッドの実装では、 前の手順で作成した macOS コード。未知の方法の場合 が呼び出された場合は、代わりにそれを報告します。

MainFlutterWindow.swift
batteryChannel.setMethodCallHandler { (call, result) in
  switch call.method {
  case "getBatteryLevel":
    guard let level = getBatteryLevel() else {
      result(
        FlutterError(
          code: "UNAVAILABLE",
          message: "Battery level not available",
          details: nil))
     return
    }
    result(level)
  default:
    result(FlutterMethodNotImplemented)
  }
}

これで、macOS 上でアプリケーションを実行できるようになります。 デバイスにバッテリーがない場合は、 「バッテリー残量が利用できません」と表示されます。

ステップ 7: Linux プラットフォーム固有の実装を追加する

この例では、upower開発者のヘッダー。 これはおそらくディストリビューションから入手できます。たとえば、次のとおりです。

sudo apt install libupower-glib-dev

まず、エディターで Flutter アプリの Linux ホスト部分を開きます。 お好みの。以下の手順は、Visual Studio Code を使用したものです。 「C/C++」および「CMake」拡張機能がインストールされていますが、他の IDE 用に調整できます。

  1. Visual Studio コードを起動します。

  2. を開きますリナックスプロジェクト内のディレクトリ。

  3. 選ぶはいプロンプトで次のように尋ねます。Would you like to configure project "linux"?。 これにより、C++ オートコンプリートが有効になります。

  4. ファイルを開くmy_application.cc

まず、必要なインクルードをファイルの先頭に追加します。 後#include <flutter_linux/flutter_linux.h:

my_application.cc
#include <math.h>
#include <upower.h>

を追加FlMethodChannel_MyApplication構造体:

my_application.cc
struct _MyApplication {
  GtkApplication parent_instance;
  char** dart_entrypoint_arguments;
  FlMethodChannel* battery_channel;
};

必ず掃除してくださいmy_application_dispose:

my_application.cc
static void my_application_dispose(GObject* object) {
  MyApplication* self = MY_APPLICATION(object);
  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
  g_clear_object(&self->battery_channel);
  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}

を編集しますmy_application_activateメソッドと初期化battery_channelチャンネル名を使ってsamples.flutter.dev/batteryに電話をかけた直後fl_register_plugins:

my_application.cc
static void my_application_activate(GApplication* application) {
  // ...
  fl_register_plugins(FL_PLUGIN_REGISTRY(self->view));

  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
  self->battery_channel = fl_method_channel_new(
      fl_engine_get_binary_messenger(fl_view_get_engine(view)),
      "samples.flutter.dev/battery", FL_METHOD_CODEC(codec));
  fl_method_channel_set_method_call_handler(
      self->battery_channel, battery_method_call_handler, self, nullptr);

  gtk_widget_grab_focus(GTK_WIDGET(self->view));
}

次に、Linux バッテリー API を使用する C コードを追加します。 バッテリーレベルを取得します。このコードは次とまったく同じです ネイティブ Linux アプリケーションで記述することになります。

以下を新しい関数として先頭に追加しますmy_application.ccの直後G_DEFINE_TYPEライン:

my_application.cc
static FlMethodResponse* get_battery_level() {
  // Find the first available battery and report that.
  g_autoptr(UpClient) up_client = up_client_new();
  g_autoptr(GPtrArray) devices = up_client_get_devices2(up_client);
  if (devices->len == 0) {
    return FL_METHOD_RESPONSE(fl_method_error_response_new(
        "UNAVAILABLE", "Device does not have a battery.", nullptr));
  }

  UpDevice* device = (UpDevice*)(g_ptr_array_index(devices, 0));
  double percentage = 0;
  g_object_get(device, "percentage", &percentage, nullptr);

  g_autoptr(FlValue) result =
      fl_value_new_int(static_cast<int64_t>(round(percentage)));
  return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}

最後に、battery_method_call_handler参照される関数 以前の電話でfl_method_channel_set_method_call_handler。 単一プラットフォームのメソッドを処理する必要があります。getBatteryLevel、 それでそれをテストしてくださいmethod_call口論。 この関数呼び出しの実装は、 前の手順で作成した Linux コード。未知の方法の場合 が呼び出された場合は、代わりにそれを報告します。

の後に次のコードを追加しますget_battery_level関数:

flutter_window.cpp
static void battery_method_call_handler(FlMethodChannel* channel,
                                        FlMethodCall* method_call,
                                        gpointer user_data) {
  g_autoptr(FlMethodResponse) response = nullptr;
  if (strcmp(fl_method_call_get_name(method_call), "getBatteryLevel") == 0) {
    response = get_battery_level();
  } else {
    response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
  }

  g_autoptr(GError) error = nullptr;
  if (!fl_method_call_respond(method_call, response, &error)) {
    g_warning("Failed to send response: %s", error->message);
  }
}

これで、Linux 上でアプリケーションを実行できるようになります。 デバイスにバッテリーがない場合は、 「バッテリーレベルが利用できません」と表示されます。

Pigeon を使用したタイプセーフ プラットフォーム チャネル

前の例では、MethodChannelホストとクライアントの間で通信するため、 これはタイプセーフではありません。通話と受信 メッセージはホストとクライアントの宣言に依存します メッセージが機能するためには、同じ引数とデータ型が必要です。 使用できます鳩としてパッケージ化する の代替品MethodChannelでメッセージを送信するコードを生成するには 構造化されたタイプセーフな方法。

と鳩、メッセージングプロトコルが定義されています Dart のサブセット内でメッセージングを生成する Android または iOS 用のコード。より完全なものを見つけることができます 例と詳細情報pigeonpub.dev のページ。

使用する鳩一致させる必要がなくなります ホストとクライアント間の文字列 メッセージの名前とデータ型については。 サポートするもの: ネストされたクラス、グループ化 API へのメッセージ、生成 非同期ラッパーコードとメッセージの送信 どちらの方向でも。生成されたコードは読み取り可能です 間に矛盾がないことを保証します 異なるバージョンの複数のクライアント。 サポートされている言語は、Objective-C、Java、Kotlin、 および Swift (Objective-C 相互運用機能付き)。

ハトの例

鳩ファイル:

import 'package:pigeon/pigeon.dart';

class SearchRequest {
  final String query;

  SearchRequest({required this.query});
}

class SearchReply {
  final String result;

  SearchReply({required this.result});
}

@HostApi()
abstract class Api {
  @async
  SearchReply search(SearchRequest request);
}

ダーツの使用法:

import 'generated_pigeon.dart';

Future<void> onClick() async {
  SearchRequest request = SearchRequest(query: 'test');
  Api api = SomeApi();
  SearchReply reply = await api.search(request);
  print('reply: ${reply.result}');
}

プラットフォーム固有のコードを UI コードから分離する

プラットフォーム固有のコードを使用する予定がある場合 複数の Flutter アプリでは、次のことを検討してください。 コードをプラットフォーム プラグインに分割する メインアプリケーションの外側のディレクトリにあります。 見るパッケージの開発詳細については。

プラットフォーム固有のコードをパッケージとして公開する

プラットフォーム固有のコードを他の開発者と共有するには Flutter エコシステムについては、を参照してください。パッケージの公開

カスタムチャンネルとコーデック

上記以外にもd1be9d7e-efd2-4b92-aa95-8ccf3a7​​e08da、 より基本的なものを使用することもできますBasicMessageChannel基本をサポートする、 カスタム メッセージ コーデックを使用した非同期メッセージ パッシング。 専門的なものを使用することもできますBinaryCodecStringCodec、 とJSONMessageCodecクラスを使用するか、独自のコーデックを作成します。

カスタム コーデックの例も確認してください。 の中にcloud_firestoreプラグイン、 さらに多くのものをシリアル化および逆シリアル化できます デフォルトのタイプよりもタイプが異なります。

チャネルとプラットフォームのスレッド化

Flutter 向けのプラットフォーム側のチャネルを呼び出す場合、 プラットフォームのメインスレッドで呼び出します。 Flutter でプラットフォーム側向けのチャネルを呼び出す場合、 どれからでも呼び出しますIsolateそれが根ですIsolateまた背景として登録されているものIsolate。 プラットフォーム側のハンドラーはプラットフォームのメインスレッドで実行できます。 タスク キューを使用する場合は、バックグラウンド スレッドで実行することもできます。 プラットフォーム側のハンドラーを非同期的に呼び出すことができます そしてどのスレッドでも。

バックグラウンド分離からのプラグインとチャンネルの使用

プラグインとチャンネルは誰でも使用できますIsolate、 でもあのIsolateである必要があります 根Isolate(Flutterで作成したもの)または背景として登録したものIsolate根のためにIsolate

次の例は、背景を登録する方法を示しています。Isolateそうするには バックグラウンドからプラグインを使用するIsolate

import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';

void _isolateMain(RootIsolateToken rootIsolateToken) async {
  BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
  SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
  print(sharedPreferences.getBool('isDebug'));
}

void main() {
  RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
  Isolate.spawn(_isolateMain, rootIsolateToken);
}

バックグラウンドスレッドでのチャネルハンドラーの実行

チャネルのプラットフォーム側ハンドラーが バックグラウンド スレッドで実行する場合は、 タスクキューAPI。現在、この機能は iOS と Android でサポートされています。

Javaの場合:

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
  BinaryMessenger messenger = binding.getBinaryMessenger();
  BinaryMessenger.TaskQueue taskQueue =
      messenger.makeBackgroundTaskQueue();
  channel =
      new MethodChannel(
          messenger,
          "com.example.foo",
          StandardMethodCodec.INSTANCE,
          taskQueue);
  channel.setMethodCallHandler(this);
}

Kotlin の場合:

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
  val taskQueue =
      flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue()
  channel = MethodChannel(flutterPluginBinding.binaryMessenger,
                          "com.example.foo",
                          StandardMethodCodec.INSTANCE,
                          taskQueue)
  channel.setMethodCallHandler(this)
}

スウィフトの場合:

public static func register(with registrar: FlutterPluginRegistrar) {
  let taskQueue = registrar.messenger.makeBackgroundTaskQueue()
  let channel = FlutterMethodChannel(name: "com.example.foo",
                                     binaryMessenger: registrar.messenger(),
                                     codec: FlutterStandardMethodCodec.sharedInstance,
                                     taskQueue: taskQueue)
  let instance = MyPlugin()
  registrar.addMethodCallDelegate(instance, channel: channel)
}

Objective-C の場合:

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  NSObject<FlutterTaskQueue>* taskQueue =
      [[registrar messenger] makeBackgroundTaskQueue];
  FlutterMethodChannel* channel =
      [FlutterMethodChannel methodChannelWithName:@"com.example.foo"
                                  binaryMessenger:[registrar messenger]
                                            codec:[FlutterStandardMethodCodec sharedInstance]
                                        taskQueue:taskQueue];
  MyPlugin* instance = [[MyPlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}

Android の UI スレッドにジャンプする

チャネルの UI スレッド要件に準拠するには、 バックグラウンド スレッドからジャンプする必要がある場合があります Android の UI スレッドに接続してチャネル メソッドを実行します。 Android では、次のようにしてこれを実現できます。post()しているRunnableAndroid の UI スレッドへLooper、 それが原因となるRunnableで実行する メインスレッドは次の機会に。

Javaの場合:

new Handler(Looper.getMainLooper()).post(new Runnable() {
  @Override
  public void run() {
    // Call the desired channel message here.
  }
});

Kotlin の場合:

Handler(Looper.getMainLooper()).post {
  // Call the desired channel message here.
}

iOS のメインスレッドにジャンプする

チャネルのメインスレッド要件に準拠するには、 バックグラウンド スレッドからジャンプする必要がある場合があります。 チャネルメソッドを実行するiOSのメインスレッド。 iOS でこれを実行するには、ブロックメインでディスパッチキュー:

Objective-C の場合:

dispatch_async(dispatch_get_main_queue(), ^{
  // Call the desired channel message here.
});

スウィフトの場合:

DispatchQueue.main.async {
  // Call the desired channel message here.
}